/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#include <sys/types.h>
#include <sys/errno.h>
#include <setjmp.h>
#include <string.h>

#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/tiuser.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <rpcsvc/yp_prot.h>
#include "snoop.h"

extern char *dlc_header;
extern jmp_buf xdr_err;
char *ypbind_error();
char *sum_ypxfrstat();
char *sum_ypmaplist();
void detail_ypmaplist();

static void niscall(int);
static void nisreply(int);
static int detail_ypstat(void);
static int sum_ypstat(char *);

/*
 * Defines missing from 5.0 yp_prot.h
 */
#define	YPBINDPROG		((ulong_t)100007)
#define	YPBINDVERS		((ulong_t)2)
#define	YPBINDVERS_ORIG		((ulong_t)1)

/* Procedure symbols */

#define	YPBINDPROC_NULL		((ulong_t)0)
#define	YPBINDPROC_DOMAIN	((ulong_t)1)
#define	YPBINDPROC_SETDOM	((ulong_t)2)

#define	YPBIND_ERR_ERR 1		/* Internal error */
#define	YPBIND_ERR_NOSERV 2		/* No bound server for passed domain */
#define	YPBIND_ERR_RESC 3		/* System resource allocation failure */


static char *procnames_bind_short[] = {
	"NULL",			/*  0 */
	"DOMAIN",		/*  1 */
	"SETDOMAIN",		/*  2 */
};

static char *procnames_bind_long[] = {
	"Null procedure",		/*  0 */
	"Get domain name",		/*  1 */
	"Set domain name",		/*  2 */
};

static char *procnames_short[] = {
	"NULL",			/*  0 */
	"DOMAIN",		/*  1 */
	"DOMAIN_NONACK",	/*  2 */
	"MATCH",		/*  3 */
	"FIRST",		/*  4 */
	"NEXT",			/*  5 */
	"XFR",			/*  6 */
	"CLEAR",		/*  7 */
	"ALL",			/*  8 */
	"MASTER",		/*  9 */
	"ORDER",		/* 10 */
	"MAPLIST",		/* 11 */
	"NEWXFR",		/* 12 */
};

#define	MAXPROC_BIND	2
#define	MAXPROC		12

static char *procnames_long[] = {
	"Null procedure",			/*  0 */
	"Verify domain support",		/*  1 */
	"Verify domain support (broadcast)",	/*  2 */
	"Return value of a key",		/*  3 */
	"Return first key-value pair in map",	/*  4 */
	"Return next key-value pair in map",	/*  5 */
	"Request map update (old)",		/*  6 */
	"Close current map on server",		/*  7 */
	"Get all key-value pairs in map",	/*  8 */
	"Get master server",			/*  9 */
	"Get order",				/* 10 */
	"Return list of supported maps",	/* 11 */
	"Request map update",			/* 12 */
};

void
interpret_nisbind(int flags, int type, int xid, int vers, int proc, char *data,
    int len)
{
	char *line;
	char buff[YPMAXDOMAIN + 1];
	unsigned int status;

	if (proc < 0 || proc > MAXPROC_BIND)
		return;

	if (flags & F_SUM) {
		if (setjmp(xdr_err)) {
			return;
		}

		line = get_sum_line();

		if (type == CALL) {
			(void) sprintf(line, "NISBIND C %s",
			    procnames_bind_short[proc]);
			line += strlen(line);
			switch (proc) {
			case YPBINDPROC_NULL:
				break;
			case YPBINDPROC_DOMAIN:
				(void) sprintf(line, " %s",
				    getxdr_string(buff, YPMAXDOMAIN));
				break;
			case YPBINDPROC_SETDOM:
				(void) sprintf(line, " %s",
				    getxdr_string(buff, YPMAXDOMAIN));
				break;
			default:
				break;
			}
			check_retransmit(line, xid);
		} else {
			(void) sprintf(line, "NISBIND R %s ",
			    procnames_bind_short[proc]);
			line += strlen(line);
			switch (proc) {
			case YPBINDPROC_NULL:
				break;
			case YPBINDPROC_DOMAIN:
				status = getxdr_long();
				if (status == 1) {	/* success */
					(void) strcat(line, "OK");
				} else {		/* failure */
					status = getxdr_long();
					(void) sprintf(line, "ERROR=%s",
					    ypbind_error(status));
				}
				break;
			case YPBINDPROC_SETDOM:
				break;
			default:
				break;
			}
		}
	}

	if (flags & F_DTAIL) {
		show_header("NISBIND:",
		    "Network Information Service Bind", len);
		show_space();
		if (setjmp(xdr_err)) {
			return;
		}
		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
		    proc, procnames_bind_long[proc]);
		if (type == CALL) {
			switch (proc) {
			case YPBINDPROC_NULL:
				break;
			case YPBINDPROC_DOMAIN:
				(void) showxdr_string(YPMAXDOMAIN,
				    "Domain = %s");
				break;
			case YPBINDPROC_SETDOM:
				(void) showxdr_string(YPMAXDOMAIN,
				    "Domain = %s");
				(void) showxdr_hex(4, "Address=%s");
				(void) showxdr_hex(2, "Port=%s");
				(void) showxdr_u_long("Version=%lu");
				break;
			default:
				break;
			}
		} else {
			switch (proc) {
			case YPBINDPROC_NULL:
				break;
			case YPBINDPROC_DOMAIN:
				status = getxdr_u_long();
				(void) sprintf(get_line(0, 0),
				    "Status = %lu (%s)",
				    status,
				    status == 1 ? "OK":"Fail");
				if (status == 1) {
					(void) showxdr_hex(4, "Address=%s");
					(void) showxdr_hex(2, "Port=%s");
				} else {
					status = getxdr_u_long();
					(void) sprintf(get_line(0, 0),
					    "Error = %lu (%s)", status,
					    ypbind_error(status));
				}
				break;
			case YPBINDPROC_SETDOM:
				break;
			default:
				break;
			}
		}
		show_trailer();
	}
}

void
interpret_nis(int flags, int type, int xid, int vers, int proc, char *data,
    int len)
{
	char *line;
	char *dom, *map, *key;
	int transid, status;
	/* buffers are all the same size so we don't have to keep track */
	char buff1[YPMAXRECORD + 1], buff2[YPMAXRECORD + 1];
	char buff3[YPMAXRECORD + 1];

	if (flags & F_SUM) {
		if (setjmp(xdr_err)) {
			return;
		}

		line = get_sum_line();

		if (type == CALL) {
			if (proc > MAXPROC)
				(void) sprintf(line, "NIS C %d", proc);
			else
				(void) sprintf(line, "NIS C %s",
				    procnames_short[proc]);
			line += strlen(line);
			switch (proc) {
			case YPPROC_NULL:
				break;
			case YPPROC_DOMAIN:
			case YPPROC_DOMAIN_NONACK:
			case YPPROC_MAPLIST:
				/* YPMAXDOMAIN > YPMAXMAP */
				(void) sprintf(line, " %s",
				    getxdr_string(buff1, YPMAXDOMAIN));
				break;
			case YPPROC_FIRST:
				dom = getxdr_string(buff1, YPMAXDOMAIN);
				map = getxdr_string(buff2, YPMAXMAP);
				(void) sprintf(line, " %s", map);
				break;
			case YPPROC_MATCH:
			case YPPROC_NEXT:
				dom = getxdr_string(buff1, YPMAXDOMAIN);
				map = getxdr_string(buff2, YPMAXMAP);
				key = getxdr_string(buff3, YPMAXRECORD);
				(void) sprintf(line, " %s in %s", key, map);
				break;
			case YPPROC_NEWXFR:
			case YPPROC_XFR:
				dom = getxdr_string(buff1, YPMAXDOMAIN);
				map = getxdr_string(buff2, YPMAXMAP);
				(void) sprintf(line, " map %s in %s", map, dom);
				break;
			case YPPROC_CLEAR:
				break;
			case YPPROC_ALL:
			case YPPROC_MASTER:
			case YPPROC_ORDER:
				dom = getxdr_string(buff1, YPMAXDOMAIN);
				map = getxdr_string(buff2, YPMAXMAP);
				(void) sprintf(line, " map %s in %s", map, dom);
				break;
			default:
				break;
			}
			check_retransmit(line, xid);
		} else {
			if (proc > MAXPROC)
				(void) sprintf(line, "NIS R %d ", proc);
			else
				(void) sprintf(line, "NIS R %s ",
				    procnames_short[proc]);
			line += strlen(line);
			switch (proc) {
			case YPPROC_NULL:
				break;
			case YPPROC_DOMAIN:
			case YPPROC_DOMAIN_NONACK:
				(void) sprintf(line, "%s",
				    getxdr_long() ? "OK":"Fail");
				break;
			case YPPROC_MATCH:
				(void) sum_ypstat(line);
				break;
			case YPPROC_FIRST:
			case YPPROC_NEXT:
				if (sum_ypstat(line) == YP_TRUE) {
					line += strlen(line);
					(void) getxdr_string(buff1,
					    YPMAXRECORD);
					(void) sprintf(line, " key=%s",
					    getxdr_string(buff1,
					    YPMAXRECORD));
				}
				break;
			case YPPROC_NEWXFR:
			case YPPROC_XFR:
				transid = getxdr_u_long();
				status  = getxdr_long();
				(void) sprintf(line, "transid=%lu %s", transid,
				    sum_ypxfrstat(status));
				break;
			case YPPROC_CLEAR:
				break;
			case YPPROC_ALL:
				if (getxdr_u_long()) {
					(void) sum_ypstat(line);
					line += strlen(line);
					(void) sprintf(line, " key=%s",
					    getxdr_string(buff1, YPMAXRECORD));
				} else {
					(void) sprintf(line, "No more");
				}
				break;
			case YPPROC_MASTER:
				if (sum_ypstat(line) == YP_TRUE) {
					line += strlen(line);
					(void) sprintf(line, " peer=%s",
					    getxdr_string(buff1, YPMAXPEER));
				}
				break;
			case YPPROC_ORDER:
				if (sum_ypstat(line) == YP_TRUE) {
					line += strlen(line);
					(void) sprintf(line, " order=%lu",
					    getxdr_u_long());
				}
				break;
			case YPPROC_MAPLIST:
				if (sum_ypstat(line) == YP_TRUE) {
					line += strlen(line);
					(void) sprintf(line, " %s",
					    sum_ypmaplist());
				}
				break;
			default:
				break;
			}
		}
	}

	if (flags & F_DTAIL) {
		show_header("NIS:  ", "Network Information Service", len);
		show_space();
		if (setjmp(xdr_err)) {
			return;
		}
		(void) sprintf(get_line(0, 0), "Proc = %d (%s)", proc,
		    proc > MAXPROC ? "unknown" : procnames_long[proc]);
		if (type == CALL)
			niscall(proc);
		else
			nisreply(proc);
		show_trailer();
	}
}

/*
 *  Print out version 2 NIS call packets
 */

static void
niscall(int proc)
{
	switch (proc) {
	case YPPROC_NULL:
		break;
	case YPPROC_DOMAIN:
	case YPPROC_DOMAIN_NONACK:
	case YPPROC_MAPLIST:
		(void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
		break;
	case YPPROC_FIRST:
		(void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
		(void) showxdr_string(YPMAXMAP, "Map = %s");
		break;
	case YPPROC_MATCH:
	case YPPROC_NEXT:
		(void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
		(void) showxdr_string(YPMAXMAP, "Map = %s");
		(void) showxdr_string(YPMAXRECORD, "Key = %s");
		break;
	case YPPROC_NEWXFR:
		(void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
		(void) showxdr_string(YPMAXMAP, "Map = %s");
		(void) showxdr_u_long("Order = %lu");
		(void) showxdr_string(YPMAXPEER, "Peer = %s");
		(void) showxdr_u_long("Transid = %lu");
		(void) showxdr_u_long("Prog = %lu");
		(void) showxdr_string(YPMAXPEER, "Name = %s");
		break;
	case YPPROC_XFR:
		(void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
		(void) showxdr_string(YPMAXMAP, "Map = %s");
		(void) showxdr_u_long("Order = %lu");
		(void) showxdr_string(YPMAXPEER, "Peer = %s");
		(void) showxdr_u_long("Transid = %lu");
		(void) showxdr_u_long("Prog = %lu");
		(void) showxdr_u_long("Port = %lu");
		break;
	case YPPROC_CLEAR:
		break;
	case YPPROC_ALL:
	case YPPROC_MASTER:
	case YPPROC_ORDER:
		(void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
		(void) showxdr_string(YPMAXMAP, "Map = %s");
		break;
	default:
		break;
	}
}

/*
 *  Print out version 2 NIS reply packets
 */

void
nisreply(int proc)
{
	unsigned int xfrstat, more;

	switch (proc) {
	case YPPROC_NULL:
		break;
	case YPPROC_DOMAIN:
	case YPPROC_DOMAIN_NONACK:
		(void) sprintf(get_line(0, 0), "Result=%s",
		    getxdr_u_long() ? "OK":"Fail");
		break;
	case YPPROC_MATCH:
		(void) detail_ypstat();
		(void) showxdr_string(YPMAXRECORD, "Value = %s");
		break;
	case YPPROC_FIRST:
	case YPPROC_NEXT:
		(void) detail_ypstat();
		(void) showxdr_string(YPMAXRECORD, "Value = %s");
		(void) showxdr_string(YPMAXRECORD, "Key = %s");
		break;
	case YPPROC_NEWXFR:
	case YPPROC_XFR:
		(void) showxdr_u_long("Transid = %lu");
		xfrstat = getxdr_u_long();
		(void) sprintf(get_line(0, 0), "Transfer status = %lu (%s)",
		    xfrstat, sum_ypxfrstat(xfrstat));
		break;
	case YPPROC_CLEAR:
		break;
	case YPPROC_ALL:
		more = getxdr_u_long();
		(void) sprintf(get_line(0, 0), "More = %s",
		    more ? "true" : "false");
		if (more) {
			(void) detail_ypstat();
			(void) showxdr_string(YPMAXRECORD, "Value = %s");
			(void) showxdr_string(YPMAXRECORD, "Key = %s");
		}
		break;
	case YPPROC_MASTER:
		(void) detail_ypstat();
		(void) showxdr_string(YPMAXPEER, "Peer = %s");
		break;
	case YPPROC_ORDER:
		(void) detail_ypstat();
		(void) showxdr_u_long("Order=%lu");
		break;
	case YPPROC_MAPLIST:
		(void) detail_ypstat();
		detail_ypmaplist();
		break;
	default:
		break;
	}
}

char *
sum_ypxfrstat(int status)
{
	static char buff [16];

	switch (status) {
	case   1:	return ("Success");
	case   2:	return ("Master's version not newer");
	case  -1:	return ("Can't find server for map");
	case  -2:	return ("No such domain");
	case  -3:	return ("Resource allocation failure");
	case  -4:	return ("RPC failure talking to server");
	case  -5:	return ("Can't get master address");
	case  -6:	return ("NIS server/map db error");
	case  -7:	return ("Bad arguments");
	case  -8:	return ("Local dbm operation failed");
	case  -9:	return ("Local file I/O operation failed");
	case -10:	return ("Map version skew during transfer");
	case -11:	return ("Can't send clear req to local ypserv");
	case -12:	return ("No local order number in map");
	case -13:	return ("Transfer error");
	case -14:	return ("Transfer request refused");
	default:
		(void) sprintf(buff, "(%d)", status);
		return (buff);
	}
	/* NOTREACHED */
}

static int
sum_ypstat(char *line)
{
	ulong_t status;
	char *str;
	char buff[16];

	status = getxdr_u_long();
	switch (status) {
	case YP_TRUE:	str = "OK";			break;
	case YP_NOMORE:	str = "No more entries";	break;
	case YP_FALSE:	str = "Fail";			break;
	case YP_NOMAP:	str = "No such map";		break;
	case YP_NODOM:	str = "No such domain";		break;
	case YP_NOKEY:	str = "No such key";		break;
	case YP_BADOP:	str = "Invalid operation";	break;
	case YP_BADDB:	str = "Bad database";		break;
	case YP_YPERR:	str = "Server error";		break;
	case YP_BADARGS:str = "Bad args";		break;
	case YP_VERS:	str = "Version mismatch";	break;
	default:	(void) sprintf(buff, "(%lu)", status);
			str = buff;
			break;
	}
	(void) strcpy(line, str);
	return ((int)status);
}

static int
detail_ypstat(void)
{
	ulong_t status;
	char buff[32];


	status = sum_ypstat(buff);
	(void) sprintf(get_line(0, 0), "Status = %d (%s)", status, buff);

	return ((int)status);
}

char *
sum_ypmaplist(void)
{
	static char buff[YPMAXMAP + 1];
	volatile int maps = 0;

	if (setjmp(xdr_err)) {
		(void) sprintf(buff, "%d+ maps", maps);
		return (buff);
	}

	while (getxdr_long()) {
		(void) getxdr_string(buff, YPMAXMAP);
		maps++;
	}

	(void) sprintf(buff, "%d maps", maps);
	return (buff);
}

void
detail_ypmaplist(void)
{
	volatile int maps = 0;

	if (setjmp(xdr_err)) {
		(void) sprintf(get_line(0, 0),
		    " %d+ maps. (Frame is incomplete)", maps);
		return;
	}

	(void) sprintf(get_line(0, 0), "Map list");

	while (getxdr_long()) {
		(void) showxdr_string(YPMAXMAP, "  %s");
		maps++;
	}

	(void) sprintf(get_line(0, 0), "%d maps", maps);
}

char *
ypbind_error(int err)
{
	static char buff[16];

	switch (err) {
	case YPBIND_ERR_ERR:	return ("Internal error");
	case YPBIND_ERR_NOSERV:	return ("Internal error");
	case YPBIND_ERR_RESC:	return ("Resource allocation fail");
	default:
		(void) sprintf(buff, "(%d)", err);
		return (buff);
	}
	/* NOTREACHED */
}
